use assembler::program_parsers::program;
use nom::types::CompleteStr;

use crate::{assembler, vm::VM};
use std::{
    io::{stdin, stdout, BufRead, Write},
    num::ParseIntError,
    path::Path,
};

pub struct Repl {
    command_buffer: Vec<String>,
    vm: VM,
}

impl Repl {
    pub fn new() -> Self {
        Self {
            vm: VM::new(),
            command_buffer: vec![],
        }
    }

    fn parse_hex(&self, i: &str) -> Result<Vec<u8>, ParseIntError> {
        let split = i.split(" ").collect::<Vec<_>>();

        let mut results = vec![];

        for hex_string in split {
            let byte = u8::from_str_radix(&hex_string, 16);
            match byte {
                Ok(result) => results.push(result),
                Err(e) => return Err(e),
            }
        }

        Ok(results)
    }

    pub fn run(&mut self) {
        println!("Welcome to Iridium! Let's be productive!");
        let stdin = stdin();
        let mut load_file = false;

        for line in stdin.lock().lines() {
            let line = line.unwrap().trim().to_string();
            if load_file {
                load_file = false;
                let contents =
                    std::fs::read(&line).expect(&format!("unable to read file {}", line));
                let contents = String::from_utf8(contents).expect("unable to read file as utf8");

                let program = match program((contents.as_str()).into()) {
                    Ok(p) => p.1,
                    Err(e) => {
                        println!("Unable to parse input: {:?}", e);
                        continue;
                    }
                };

                self.vm.program.append(&mut program.to_bytes());
                continue;
            }

            match line.as_str() {
                ".program" | ".p" => {
                    println!("Listing instructions currently in VM's program vector:");
                    for instruction in &self.vm.program {
                        println!("{}", instruction);
                    }
                    println!("End of Program Listing");
                }
                ".registers" | ".r" => {
                    println!("Listing registers and all contents:");
                    println!("{:#?}", self.vm.registers);
                    println!("End of Register Listing")
                }
                ".history" | ".h" => {
                    for command in &self.command_buffer {
                        println!("{}", command);
                    }
                }
                ".quit" | ".q" | ".exit" | ".e" => {
                    println!("exit repl!");
                    return;
                }
                ".load_file" | ".l" => {
                    print!("Please enter the path to the file you wish to load: ");
                    stdout().flush().expect("Unable to flush stdout");
                    load_file = true;
                }
                input => {
                    let program = match program(input.into()) {
                        Ok(p) => p.1,
                        Err(_) => {
                            println!("unable to parse input");
                            continue;
                        }
                    };

                    self.vm.program.append(&mut program.to_bytes());
                    self.vm.run_once();
                }
            }
            self.command_buffer.push(line)
        }
    }
}
